package com.sz.youban.dt.service.allinpay;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.sz.youban.dt.entity.allinpay.DtPayAuthentication;
import com.sz.youban.dt.entity.allinpay.DtPayAuthenticationLog;
import com.sz.youban.dt.entity.allinpay.DtPayInvokeRecords;
import com.sz.youban.dt.entity.allinpay.DtPayNotifyRecords;
import com.sz.youban.dt.entity.allinpay.DtPayRecords;
import com.sz.youban.dt.entity.pay.PayNotifyStatus;
import com.sz.youban.dt.entity.pay.PayStatus;
import com.sz.youban.dt.entity.result.ResultData;
import com.sz.youban.dt.interfs.allinpay.AllinpayService;
import com.sz.youban.dt.interfs.allinpay.IDtPayAuthenticationService;
import com.sz.youban.dt.interfs.allinpay.IDtPayRecordsService;
import com.sz.youban.dt.model.PairDef;
import com.sz.youban.dt.service.allinpay.mapper.DtPayAuthenticationMapper;
import com.sz.youban.dt.service.allinpay.mapper.DtPayInvokeRecordsMapper;
import com.sz.youban.dt.service.allinpay.mapper.DtPayNotifyRecordsMapper;
import com.sz.youban.dt.service.allinpay.helpper.AllinpayUtil;
import com.sz.youban.dt.service.utils.GO;
import com.sz.youban.dt.service.utils.GV;
import com.sz.youban.tonglian.entity.common.TongLianAuthenticationParam;
import com.sz.youban.tonglian.entity.common.TongLianPayMsg;
import com.sz.youban.tonglian.entity.common.TongLianPayNecessayParam;
import com.sz.youban.tonglian.entity.common.TongLianPayResultCode;
import com.sz.youban.tonglian.entity.common.TongLianPaySysParam;
import com.sz.youban.tonglian.service.TranxServiceImpl;
import com.sz.youban.tonglian.service.TranxServiceInstance;

/**
 * Created by liyang on 2017/6/13.
 */
@Service
@Transactional
public class AllinpayServiceImpl implements AllinpayService {

    @Value("${develop}")
    private Boolean develop;

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private IDtPayRecordsService payRecordsService;

    @Autowired
    private IDtPayAuthenticationService authenticationService;

    @Autowired
    private DtPayInvokeRecordsMapper invokeRecordsMapper;

    @Autowired
    private DtPayAuthenticationMapper authenticationMapper;
    @Autowired
    private DtPayNotifyRecordsMapper payNotifyRecordsMapper;

    /**
     * 功能：实时单笔代收付，100011是实时单笔代收，100014是实时单笔代付
     * @throws Exception
     */
    @Override
    public ResultData singleTranxReceivePay(String bankCode, String accountNo, String accountName, String amount, String userId, String phone,
                                                String province, String city, String cardId, String trxCode, String channel, String orderNo, String invokeUrl) throws Exception
    {
        TranxServiceImpl tranxService = TranxServiceInstance.newInstance();

        TongLianPayNecessayParam nessaryParam = AllinpayUtil.getTongLianNessaryParam
        		(develop, accountNo, accountName, amount, bankCode, city, phone, province, trxCode, userId);

        TongLianPaySysParam paySysParam = AllinpayUtil.getTongLianSystemObj(develop,trxCode);
        //==========1，生成交易流水号==========
        String dealNo = paySysParam.getMerchantId()+"youban-dt"+System.currentTimeMillis();
        nessaryParam.setDealNo(dealNo);

        //==========2，存储交易记录=====直存=====
        {
            DtPayRecords payRecords = new DtPayRecords();
            payRecords.setChannel(GV.i(channel));
            payRecords.setType(GV.i(trxCode));
            payRecords.setDealNo(dealNo);   //交易流水号
            payRecords.setAmount(GO.bd(amount).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP));
            payRecords.setMerchantId(paySysParam.getMerchantId());
            payRecords.setOrderNo(orderNo);
            payRecords.setRealName(accountName);
            payRecords.setCardId(cardId);
            payRecords.setPhone(phone);
            payRecords.setBankNo(accountNo);
            payRecords.setBankNo(bankCode);
            payRecords.setBankProv(province);
            payRecords.setBankCity(city);

            //添加 orderNo 重复校验
            DtPayRecords existPayRecords = payRecordsService.getRecordsByOrderNo(orderNo);
            //已存在该打款订单编号，直接返回
            if(existPayRecords != null){
                ResultData rsData = new ResultData();
                rsData.setCode(TongLianPayResultCode.DEAL_ERROR);
                rsData.setMsg("该打款订单编号已存在。");
                //组装info内部数据
                {
                    Map<String, String> map = new HashMap<>();
                    String settleDay = "";
                    if(null != existPayRecords.getSettleDay()){
                        settleDay = existPayRecords.getSettleDay();
                    }
                    map.put("settleDay",settleDay);
                    map.put("dealNo",dealNo);
                    map.put("orderNo",orderNo);
                    rsData.setInfo(map);
                }

                return rsData;
            }

            int result = payRecordsService.saveReceiveAndPayRecord(payRecords);
            if(result > 0){
                log.info("----------实时单笔代收付请求记录存储成功,交易流水号为： {}" , dealNo);
            }else{
                log.info("----------实时单笔代收付请求记录存储失败,交易流水号为： {}" , dealNo);
            }
            //保存回调给app的地址信息
            insertInvokeUrl(invokeUrl,dealNo);
        }

        //此处异常不应影响交易记录的存储
        TongLianPayMsg payMsg = new TongLianPayMsg();
        try
        {
            //==========3，整理报文，向通联发起请求==========
            payMsg =  tranxService.singleTranxNew(nessaryParam, false, paySysParam);
            //========4，根据返回结果修改dt_pay_record 中 status 和 desc===========
            int result = changePayRecordStatus(dealNo,payMsg);
            if(result > 0)
            {
                log.info("----------实时单笔代收付请求,修改交易状态成功,交易流水号为： {}" , dealNo);
            }
            else
            {
                log.warn("----------实时单笔代收付请求,修改交易状态失败,交易流水号为： {}" , dealNo);
            }
        }
        catch (Exception e)
        {
            log.error("==============ex:{}",e);
            payMsg = new TongLianPayMsg();
            payMsg.setResultCode(TongLianPayResultCode.DEAL_ERROR);
            payMsg.setResultMsg("交易失败，请联系支付管理员查看交易记录");
        }

        ResultData rsData = new ResultData();
        rsData.setCode(payMsg.getResultCode());
        rsData.setMsg(payMsg.getResultMsg());

        //组装info内部数据
        {
            Map<String, String> map = new HashMap<>();
            String settleDay = "";
            if(null != payMsg.getSettleDay()){
                settleDay = payMsg.getSettleDay();
            }
            map.put("settleDay",settleDay);
            map.put("dealNo",dealNo);
            map.put("orderNo",orderNo);
            rsData.setInfo(map);
        }
    	//保存交易成功或失败的数据为待通知的记录，便于通知调用方（处理和通联调用时间较长，但调用方超时）
        {
        	if(TongLianPayResultCode.DEAL_ING != payMsg.getResultCode())
        	{
        		Date now = new Date();
        		DtPayNotifyRecords addNotifyItem = new DtPayNotifyRecords();
                addNotifyItem.setDealNo(dealNo);
                addNotifyItem.setAmount(GO.bd(amount).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)); 
                addNotifyItem.setOrderNo(orderNo);
                addNotifyItem.setStatus(PayNotifyStatus.NOTIFY_NO.getStatus());//未通知
                addNotifyItem.setSubTime(now);
                addNotifyItem.setTsp(now);
                payNotifyRecordsMapper.insert(addNotifyItem);
        	}
        	 
        }
        return rsData;
    }

    @Override
    public PairDef<ResultData, Map<String, String>> authentication(String cardId, String accountName, String accountNo, String phone) {

    	log.info("--------进入authentication方法，参数为----accountName={},accountName={}，accountNo={}", new Object[]{accountName,cardId,accountNo});
    	
        TranxServiceImpl tranxService = TranxServiceInstance.newInstance();

        PairDef<ResultData, Map<String, String>> pd = new PairDef<ResultData, Map<String, String>>();
        ResultData rsData = new ResultData();

        //----------1，获取商户号参数----------
        TongLianAuthenticationParam authenticationParam = AllinpayUtil.getTongLianAuthenticationObj(develop);

        //----------2，生成交易流水号----------
        String dealNo = authenticationParam.getMerchantNo()+"youban-dt"+System.currentTimeMillis();

        //----------3，查询数据库备份，看此信息是否已经验证过----------
        DtPayAuthentication payAuthentication  = getAuthentication(cardId,accountName,accountNo);
        if(payAuthentication != null){
            rsData.setCode(300);
            rsData.setMsg("交易成功");
            pd.setFirst(rsData);
            Map<String,String> map= new HashMap<String,String>();
            map.put("dealNo", payAuthentication.getRequestNo());
            pd.setSecond(map);

            return pd;
        }

        //----------4，请求验证处理----------
        TongLianPayMsg payMsg = tranxService.dealBankVerify(authenticationParam.getUrl(), cardId, accountName, accountNo,
        		authenticationParam.getMerchantNo(),authenticationParam.getDealKey(), dealNo);
        log.info("----------请求dealBankVerify方法，返回的结果为：{}",payMsg.toString());

        //----------5，添加请求记录----------
        DtPayAuthenticationLog authenticationLog = new DtPayAuthenticationLog();
        {
            authenticationLog.setAccountName(accountName);
            authenticationLog.setCardId(cardId);
            authenticationLog.setAccountNo(accountNo);
            authenticationLog.setPhone(phone);
            authenticationLog.setMerchantId(authenticationParam.getMerchantNo());
            authenticationLog.setRequestNo(dealNo);
            authenticationLog.setResultCode(payMsg.getResultCode());
            authenticationLog.setResultMsg(payMsg.getResultMsg());
            authenticationService.saveAuthenticationLog(authenticationLog);
            log.info("----------添加三要素验证请求日志，成功----------");
        }

        //----------6，验证成功，存储数据备用----------
        if(payMsg.getResultCode() == 300){
            DtPayAuthentication authentication = new DtPayAuthentication();
            authentication.setAccountName(accountName);
		    authentication.setCardId(cardId);
		    authentication.setAccountNo(accountNo);
		    authentication.setPhone(phone);
		    authentication.setMerchantId(authenticationParam.getMerchantNo());
		    authentication.setRequestNo(dealNo);
        	authenticationService.saveAuthentication(authentication);
        	log.info("----------已对三要素验证成功的数据进行备份存储----------");
        }

        //----------7，组织返回参数----------
        rsData.setCode(payMsg.getResultCode());
        rsData.setMsg(payMsg.getResultMsg());
        pd.setFirst(rsData);
        Map<String,String> map= new HashMap<String,String>();
        map.put("dealNo", dealNo);
        pd.setSecond(map);
        return pd;
    }

    private DtPayAuthentication getAuthentication(String cardId, String accountName, String accountNo) {

        DtPayAuthentication payAuthentication = authenticationMapper.getAuthentication(cardId, accountName, accountNo);

        return payAuthentication;
    }


    public int changePayRecordStatus(String dealNo, TongLianPayMsg payMsg){
        int status = PayStatus.DEAL_ING.getStatus();
        int resulCode = payMsg.getResultCode();
        String desc = payMsg.getResultMsg();
        String settleDay = null;
        if(null != payMsg.getSettleDay()){
            settleDay = payMsg.getSettleDay();
        }

        if(resulCode == TongLianPayResultCode.DEAL_SUCCESS){
            status = PayStatus.DEAL_SUCC.getStatus();
        }else if (resulCode == TongLianPayResultCode.DEAL_ERROR){
            status = PayStatus.DEAL_ERROR.getStatus();
        }

        int result = payRecordsService.changePayRecordStatus(dealNo, status , desc, settleDay);

        return result;
    }

    
    /**
     * @param url
     * 保存回调地址信息
     * url:回调地址
     * dealNo:交易流水号
     */
    private void insertInvokeUrl(String url,String dealNo)
    {
    	DtPayInvokeRecords invokeRecords = new DtPayInvokeRecords();
    	invokeRecords.setUrl(url);
    	invokeRecords.setTsp(new Date());
    	invokeRecords.setDealNo(dealNo);
    	invokeRecordsMapper.insert(invokeRecords);
    }
    
}
